
/***************************************************************************
 *   boque 
 *                                                                         *
 *   Copyright (C) 2010 by kuerant                                         *
 *                         <kuerant@gmail.com>                             *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This library is distributed in the hope that it will be usefull, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
 *   Library General Public License for more details.                      *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston,                *
 *   MA 02111-1307, USA                                                    *
 ***************************************************************************/


#include "connection.hpp"
#include <vector>
#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>
#include <boost/algorithm/string/trim.hpp>

#include <iostream>

#include "bocat/common/base64.hpp"
#include "bocat/common/logging.hpp"


namespace boque {
namespace trasio {

connection::connection(boost::asio::io_service& io_service, Handler* handler_p)
	: strand_(io_service),
	socket_(io_service),
	_handler_p(handler_p)
{
}

boost::asio::ip::tcp::socket& connection::socket()
{
	return socket_;
}

void connection::start()
{
	this->waitRequest();
}

void connection::waitRequest()
{
	boost::asio::async_read_until(socket_, request_, "\r\n",
			strand_.wrap(
				boost::bind(&connection::handle_read, shared_from_this(),
					boost::asio::placeholders::error,
					boost::asio::placeholders::bytes_transferred)));
}

void connection::handle_read(const boost::system::error_code& e,
		std::size_t bytes_transferred)
{
	if ( e ) {
		// ERROR
		// Initiate graceful connection closure.
		boost::system::error_code ignored_ec;
		socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
		return;
	}

	std::istream    request_stream( &request_ );
	std::string request_str;
	std::getline( request_stream, request_str );
	boost::trim( request_str );
	if (request_str.size() < 1) {
		// ignore empty request
		this->waitRequest();
		return;
	}

	std::string	request;
	try {
		request = bocat::common::base64_decode< std::string >( request_str );
	}
	catch( std::exception& e ) {
		std::string	ip_addr;
		unsigned short port = 0;

		boost::system::error_code ec;
		boost::asio::ip::tcp::endpoint endpoint = socket_.remote_endpoint(ec);
		if (!ec)
		{
			ip_addr = endpoint.address().to_string();
			port = endpoint.port();
		}

		bocat::common::Logger*  logger_p = bocat::common::getLogger();
		logger_p->error(__LINE__, __FILE__, "Bad data from %s:%u request '%s' -- %s", ip_addr.c_str(), port, request_str.c_str(), e.what() ); 

		// wait next request
		this->waitRequest();

		return;
	}

	// handle request
	int	rc = 0;
	try {
		rc = _handler_p->handle( request );
	}
	catch( std::exception& e ) {
		std::string	ip_addr;
		unsigned short port = 0;

		boost::system::error_code ec;
		boost::asio::ip::tcp::endpoint endpoint = socket_.remote_endpoint(ec);
		if (!ec)
		{
			ip_addr = endpoint.address().to_string();
			port = endpoint.port();
		}

		bocat::common::Logger*  logger_p = bocat::common::getLogger();
		logger_p->error(__LINE__, __FILE__, "fail to handle %s:%u request '%s' -- %s", ip_addr.c_str(), port, request_str.c_str(), e.what() ); 

		// wait next request
		this->waitRequest();

		return;
	}

	if (rc >= 0) {
		this->waitRequest();
	}
	else {
		// ERROR
		// Initiate graceful connection closure.
		boost::system::error_code ignored_ec;
		socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
	}
}

void connection::handle_write(const boost::system::error_code& e)
{
	if (!e)
	{
		this->waitRequest();
	}
	else {
		// Initiate graceful connection closure.
		boost::system::error_code ignored_ec;
		socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
	}

	// No new asynchronous operations are started. This means that all shared_ptr
	// references to the connection object will disappear and the object will be
	// destroyed automatically after this handler returns. The connection class's
	// destructor closes the socket.
}

} // namespace trasio
} // namespace boque
